#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<ctype.h>

using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;
	
//全局的命令行参数表
char* gargv[argvnum]; // ls -1 -a -n nullptr  放在vecttor里面
int gargc = 0;

//全局的变量
int lastcode=0;

//我系统的环境变量
char* genv[envnum];

//全局的当前shell工作路径
char pwd[basesize];
char pwdenv[basesize];

//全局变量与重定向有关
#define NoneRedir   0
#define InputRedir  1
#define OutputRedir 2
#define AppRedir    3

int redir = NoneRedir;
char* filename =nullptr;

//"   " file.txt
#define TrimSpace(pos) while(0){\
	while(isspace(*pos)){\
		pos++;\
	}\
}

string GetUserName()
{
	string name = getenv("USER");
	return name.empty() ? "None" : name;  //也可以直接return getenv("USER")
}

string GetHostName()  //主机名
{
	string hostname = getenv("HOSTNAME");
	return hostname.empty() ? "None" : hostname;
}

string GetPwd()
{
	if(nullptr == getcwd(pwd,sizeof(pwd))) return "None";
	snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
	putenv(pwdenv);  //PWD==xxx   导入环境变量
	return pwd;
	
//	string pwd = getenv("PWD"); 获取路径不能从环境变量获取，要从系统中获取
//	return pwd.empty() ? "None" : pwd;
}

string LastDir()
{
	string curr = GetPwd();
	if(curr=="/"||curr=="None")return curr;

	//home/whb/xxx
	size_t pos = curr.rfind("/");
	if(pos==std::string::npos)return curr;

	return curr.substr(pos+1);
}

string MakeCommandLine() //输出命令行 / 格式化输出
{
        // [yxd@hcss-ecs-16a6:~/myshell]$         用户名 主机名 pwd
	char command_line[basesize];		
	snprintf(command_line,basesize,"[%s@%s %s]# ",\
			GetUserName().c_str(),GetHostName().c_str(),LastDir().c_str());
	return command_line;
}

void PrintCommandLine()   //1.打印 命令行提示符
{
	printf("%s",MakeCommandLine().c_str());
	fflush(stdout);
	
}

bool GetCommandLine(char command_buffer[],int size)
{
     //我们要将用户输入的命令行当作一个完整的字符串 ls -a -l
     //  getline(c) 可以 但这里为了显示出更多的特性使用fgets

	char* result =fgets(command_buffer,size,stdin);  //fgets 获取字符串command_buffer 大小size 从stdin流里
	if(!result)
	{
		return false;
	}
	command_buffer[strlen(command_buffer)-1] = 0;  // 字符串最后一个改为0
	if(strlen(command_buffer)==0)return false;
	return true;
	
}	
			
void ParseCommandLine(char command_buffer[],int len)
{
	(void)len; //不想使用len
	memset(gargv,0,sizeof(gargv));
	gargc=0;

	//重定向
	redir = NoneRedir;
	filename = nullptr;

	// "ls -a -l -n"
	// "ls -a -l -n" > file.txt
	// "ls -a -l -n" < file.txt
	// "ls -a -l -n" >> file.txt
	int end =  len - 1;
	while(end>=0)
	{
		if(command_buffer[end]=='<')
		{
			redir = InputRedir;
			command_buffer[end] = 0;
			filename = &command_buffer[end]+1;
			TrimSpace(filename);
			break;
		}
		else if(command_buffer[end]=='>')
		{
			if(command_buffer[end-1]=='>')
			{
			redir = AppRedir;
			command_buffer[end]=0;
			command_buffer[end-1]=0;
			filename=&command_buffer[end]+1;
			TrimSpace(filename);
			break;
			}
		        else
	                {
			redir=OutputRedir;
			command_buffer[end]=0;
			filename=&command_buffer[end]+1;
			TrimSpace(filename);
			break;
	           	}
		}
		else
		{
			end--;
		}
	}
	
	// "ls -a -l -n"
	const char* sep = " ";
	gargv[gargc++] = strtok(command_buffer,sep);	
	
	// =是刻意写的 走到最后是返回nullptr 为假
	while((bool)(gargv[gargc++] = strtok(nullptr,sep))); // 分割上一个字符串
	gargc--;
}
			     
void debug()
{
	printf("argc:%d\n,gargc");
	for(int i = 0 ;gargv[i];i++)
	{
		printf("argv[%d]:%s\n",i,gargv[i]);
	}
}

//在shell中有些命令必须由 子进程来执行
//        有些命令    不能由子进程来执行 ，要由shell自己执行 --- 内建命令 built command
bool ExecuteCommand()
{
	// 让子进程进行执行
	pid_t id=fork();
	if(id<0)
	{
		return false;
	}
	 if(id==0)
	{
		//重定向的工作子进程来做！

		//子进程 
		//1.执行命令 exec*
		execvpe(gargv[0],gargv,genv);                     //gargv[0]就是保存的就是名称
		//2.退出  exit()  只有替换失败了才会exit()
		exit(1); 
	}
	
		//父进程
		int status =0;
		pid_t rid=waitpid(id,&status,0);
		if(rid>0)
		{
			if(WIFEXITED(status))
			{
				lastcode=WEXITSTATUS(status);
			}
			else
			{
				lastcode=100;
			}
			
		return true;
		}
		return false;
		         //非阻塞等待rid不会=0		
}	

void AddEnv(const char* item)
{
	int index=0;
	while(genv[index])
	{
		index++;
	}

	genv[index] = (char*)malloc(strlen(item)+1);
	strncpy(genv[index],item,strlen(item)+1);
	genv[++index] = nullptr;
}
//shell自己执行命令，本质是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{
	if(strcmp(gargv[0],"cd")==0)
	{
		//内建命令
		if(gargc==2)
		{
			chdir(gargv[1]);
			lastcode=0;
		}
		else
		{
			lastcode=1;
		}
		return true;  //return trye就不会创建子进程了
	}
	else if(strcmp(gargv[0],"export")==0)
	{
		//export也是内建命令
		if(gargc==2)
		{
			AddEnv(gargv[1]);
			lastcode=0;
		}
		else
		{
			lastcode=2;
		}
		return true;
	}
	else if(strcmp(gargv[0],"env")==0)
	{
		for(int i=0 ; genv[i];i++)
		{
			printf("%s\n",genv[i]);
		}
		lastcode=0;
		return true;
	}
	else if(strcmp(gargv[0],"echo")==0)
	{
	if(gargc==2)
	{
	//echo $?
	//echo hello
	if(gargv[1][0]=='$')
	{
		if(gargv[1][1]=='?')
		{
			printf("%d\n",lastcode);
			lastcode=0;
		}
	}
	else
	{
		printf("%s\n",gargv[1]);
			lastcode=0;
	}
	}
	else
	{
		lastcode=3;
	}
	return true;
	}
	return false;
}

//作为一个shell 获取环境变量一个从系统的配置来
////直接从父shell中获取环境变量
void InitEnv()
{
	extern char** environ;
	int index=0;
	while(environ[index])
	{
		genv[index]=(char*)malloc(strlen(environ[index])+1);
		strncpy(genv[index],environ[index],strlen(environ[index])+1);
		index++;
	}
	genv[index]=nullptr;
}

int main()
{
	InitEnv();
	char command_buffer[basesize]; //用户命令
	while(true)
	{
        PrintCommandLine();    //1.命令行提示符
	
//command_buffer 是一个output输出型参数
        if(!GetCommandLine(command_buffer,basesize))    //2.获取用户命令
	{
		continue;
	}
	//printf("%s\n",command_buffer); //为了测试接口获取命令行是否正确 不需要写这一行
  
        ParseCommandLine(command_buffer,strlen(command_buffer));  //3.分析命令
	
	printf("command start:%s\n",command_buffer);
	printf("redir:%d\n",redir);
	printf("filename end:%s\n",filename);

	//检查是否是内建命令
	if(CheckAndExecBuiltCommand())
	{
		continue; //如果是内建命令则 跳过循环 不用往后走了
	}

        ExecuteCommand();    //4.执行命令
	
	}
	return 0;
}
